use std::rc::Rc;
use std::collections::HashMap;

type Table = HashMap<String, Vec<String>>;

fn show(table : &Table) {
    for (artist, works) in table {
        println!("works by {}:", artist);
        for work in works {
            println!("  {}", work)
        }
    }
}

fn show_sort(table: &mut Table) {
    for (artist, works) in table {
        println!("works by {}:", artist);
        works.sort();
        for work in works {
            println!("  {}", work);
        }
    }
}

fn main() {
    println!("Hello, world!");
    let s = Rc::new("hello world!".to_string());
    let t = s.clone();
    let u = s.clone();
    assert!(s.contains("hello"));
    assert_eq!(t.find("world"), Some(6));
    println!("{} are quite chewy, almost bouncy, but lack flavour", u);
    // s.push_str(" and beyond");

    let mut table = Table::new();
    table.insert("Gesualdo".to_string(),
                 vec!["many madrigals".to_string(),
                      "Tenebrae Responsoria".to_string()]);

    table.insert("cellini".to_string(),
                 vec!["Perseus with the head of Medusa".to_string(),
                      "a salt cellar".to_string()]);

    table.insert("Caravaggio".to_string(),
                    vec!["Medusa".to_string(),
                        "The Musicians".to_string()]);

    show(&table);
    assert_eq!(table["Gesualdo"][0], "many madrigals");
    show_sort(&mut table);
    show(&table);
    unsafe {
        println!("STATE: {}", STATE);
        mod_p(&100);
        println!("STATE: {}", STATE);
    }

    let mut st = StringTable::new();
    st.push("hello".to_string());
    st.push("world".to_string());
    st.push("rust".to_string());
    st.push("c".to_string());
    st.push("c++".to_string());
    st.push("java".to_string());
    st.push("python".to_string());
    st.push("javascript".to_string());
    st.push("typescript".to_string());
    st.push("go".to_string());
    st.push("kotlin".to_string());
    st.push("swift".to_string());
    st.push("objective-c".to_string());
    st.push("c#".to_string());
    st.push("f#".to_string());
    st.push("haskell".to_string());
    st.push("scala".to_string());
    st.push("clojure".to_string());
    st.push("elixir".to_string());
    st.push("erlang".to_string());
    st.push("lua".to_string());
    st.push("perl".to_string());
    st.push("ruby".to_string());
    st.push("php".to_string());
    st.push("html".to_string());
    st.push("css".to_string());
    st.push("sql".to_string());
    st.push("nosql".to_string());
    st.push("mongodb".to_string());
    st.push("cassandra".to_string());
    st.push("redis".to_string());
    st.push("memcached".to_string());
    st.push("rabbitmq".to_string());

    println!("find_index: {}", st.find_index("rust").unwrap());
    println!("find_by_prefix: {}", st.find_by_prefix("ru").unwrap());

    let success = st.remove("rust");
    println!("remove rust: {}", success);
    println!("to_string: {}", st.to_string());


    
}

static mut STATE: &i32 = &10;

fn mod_p(p: &'static i32) {
    unsafe {
        STATE = p;
    }
}

struct StringTable {
    elements: Vec<String>,
}

impl StringTable {
    fn new() -> StringTable {
        StringTable { elements: Vec::new() }
    }

    fn push(&mut self, s: String) {
        self.elements.push(s);
    }

    fn remove(&mut self, s: &str) -> bool {
        match self.find_index(s) {
            Some(i) => {
                self.elements.remove(i);
                true
            }
            None => false,
        }
    }

    fn find_index(&self, s: &str) -> Option<usize> {
        self.elements.iter().position(|e| e == s)
    }

    fn find_by_prefix(&self, prefix: &str) -> Option<&String> {
        for i in 0..self.elements.len() {
            if self.elements[i].starts_with(prefix) {
                return Some(&self.elements[i]);
            }
        }
        None
    }

    fn to_string(&self) -> String {
        ["[\"", self.elements.join("\",\"").as_str(), "\"]"].concat()
    }
}